/*
 * @brief
 * @author li yong (DogLee)
 * @email 20162439@qq.com
 * @version 1.0
 * @date 2018-08-04 10:47:44
 */
#include "sigactionhandler.h"
#include "logmanager.h"

#include "signal.h"
#include "execinfo.h"
#include "cxxabi.h"
#include "dlfcn.h"

#include <QDebug>
#include <QDateTime>

Q_GLOBAL_STATIC(QString, application)

static void runtimeDebugHandler(int sig);

static void errorCodeHandler(int sig);

SigactionHandler::SigactionHandler()
{

}

void SigactionHandler::install(QString app)
{
    *application() = app;

    struct sigaction oldAction[_NSIG];
    struct sigaction newAction;
    newAction.sa_handler = SigactionHandler::handler;

    sigaction(SIGUSR1, &newAction, &oldAction[SIGUSR1]);
    sigaction(SIGUSR2, &newAction, &oldAction[SIGUSR2]);

    sigaction(SIGILL, &newAction, &oldAction[SIGILL]);
    sigaction(SIGBUS, &newAction, &oldAction[SIGBUS]);
    sigaction(SIGFPE, &newAction, &oldAction[SIGFPE]);
    sigaction(SIGSEGV, &newAction, &oldAction[SIGSEGV]);
    sigaction(SIGABRT, &newAction, &oldAction[SIGABRT]);

    //sigaction(SIGINT, &newAction, &oldAction[SIGINT]);
    //sigaction(SIGTERM, &newAction, &oldAction[SIGTERM]);
}

void SigactionHandler::handler(int sig)
{
    switch (sig)
    {
        case SIGUSR1:
        case SIGUSR2:
            runtimeDebugHandler(sig);
            break;
        case SIGILL:
        case SIGBUS:
        case SIGFPE:
        case SIGSEGV:
        case SIGINT:
        case SIGTERM:
            errorCodeHandler(sig);
            break;
        default:
            qInfo() << "recieve a signal:" << strsignal(sig);
    }
}

void getCrashTime(FILE *log)
{
    fprintf(log,"\nBEGIN=========================================================\n");
    time_t now = time(NULL);
    struct tm* ptm;
    char tmp[128];
    ptm = localtime(&now);
    snprintf(tmp, 128, "%d-%.2d-%.2d %.2d:%.2d:%.2d",
            ptm->tm_year+1900,ptm->tm_mon+1, ptm->tm_mday,ptm->tm_hour,ptm->tm_min,ptm->tm_sec);

    fprintf(log,"Software(\"%s\") Crash At: %s \n", application()->toLocal8Bit().constData(), tmp);
}

void dump_stack(FILE *log, int sig)
{
    getCrashTime(log);
    fprintf(log, "signal is -> %s\n\n", strsignal(sig));

    void *bt[128];
    int n = backtrace(bt,sizeof(bt)/sizeof(bt[0]));

    char **info = backtrace_symbols(bt,n);
//    backtrace_symbols_fd(bt,entries,backtrace_file);
    if(!info)
        exit(-1);
    fprintf(log,"Debug Info:\n\n");

    char cmd[512];
    int len = snprintf(cmd,sizeof(cmd),"addr2line -ifC -e %s", application()->toLocal8Bit().constData());

    char *p = cmd + len;
    size_t s = sizeof(cmd) - len;

    for(int i = 0; i < n;i++)
    {
        fprintf(log," %s \n",info[i]);
        if(s > 0)
        {
            len = snprintf(p,s," %p",bt[i]);
            p += len;
            s -= len;
        }
    }
    free(info);

    fprintf(log,"\n\nSource file Info:\n\n");
    FILE *fp;
    char tmp[128];
    if((fp = popen(cmd,"r")))
    {
        while(fgets(tmp,sizeof(tmp),fp))
            fprintf(log,"%s",tmp);
        pclose(fp);
    }
    fprintf(log,"=========================================================END\n");
    fflush(log);
}

static void runtimeDebugHandler(int sig)
{
    dump_stack(stdout, sig);
}

static void errorCodeHandler(int sig)
{
    logManager->dump();

    FILE* fp = fopen("./log/backtrace", "ab+");

    dump_stack(fp, sig);
    dump_stack(stdout, sig);

    fflush(stdout);
    fflush(fp);
    fclose(fp);

    exit(EXIT_FAILURE);
}
